package cn.s1995.sloth.common.core.util;

import cn.hutool.core.util.NumberUtil;
import lombok.experimental.UtilityClass;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

/**
 * CalcUtil
 *
 * @author penf
 * @decription 数值运算相关工具类
 * @date 2019/03/01
 */
@UtilityClass
public class CalcUtil {

    /**
     * 加法
     */
    private static final String ADDITION = "+";

    /**
     * 加法
     */
    private static final String SUBTRACTION = "-";

    /**
     * 加法
     */
    private static final String MULTIPLICATION = "*";

    /**
     * 除法
     */
    private static final String DIVISION = "/";


    /**
     * 默认精度
     */
    private static final int SCALE_DEFAULT = 2;

    /**
     * 默认精度
     */
    private static final int SCALE_ZERO = 0;


    /**
     * 我的精度
     */
    private static final String ONE_HUNDRED = "100";


    public static void main(String[] args) {
        // BigDecimal value = new BigDecimal("120.44");
        // BigDecimal value2 = new BigDecimal("101.2222");
        // BigDecimal value3 = new BigDecimal("100.2222");
        // BigDecimal subtract = add(value2, value3);
        // System.out.println(subtract);

        // LocalDateTime localDateTime1 = LocalDateTime.of(2023, 11, 8, 12, 00, 00)
        //         .atZone(ZoneId.systemDefault()).toLocalDateTime();
        // System.out.println(localDateTime1);
        //
        // // 创建一个带有时区信息的 ZonedDateTime 对象
        // ZonedDateTime zonedDateTime = ZonedDateTime.of(LocalDateTime.now(), ZoneId.of(ZoneId.SHORT_IDS.get("JST")));
        //
        // // 输出带有时区信息的日期时间
        // // System.out.println("ZonedDateTime: " + zonedDateTime);
        //
        // // 格式化日期时间
        // System.out.println("格式化后的日期时间: " + zonedDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        // System.out.println("ZonedDateTime 的时区: " + zonedDateTime.getZone());
        //
        // // 获取 LocalDateTime 对象
        // LocalDateTime localDateTime = LocalDateTime.now();
        //
        // // 输出不带时区信息的日期时间
        // System.out.println("LocalDateTime: " + localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));


        // Instantiates two LocalDateTime
        // and a ZonedId
        LocalDateTime da_ti1 = LocalDateTime.parse("2005-10-05T10:10:10");
        LocalDateTime da_ti2 = LocalDateTime.now();

        ZoneId zo = ZoneId.of(ZoneId.SHORT_IDS.get("JST"));
        ZonedDateTime zonedDateTime = da_ti1.atZone(zo);

        // Display da_ti1, da_ti2
        // and zo
        System.out.println("LocalDateTime da_ti1 and da_ti2:");
        System.out.println("da_ti1:" + da_ti1);
        System.out.println("da_ti2:" + da_ti2);
        System.out.println("ZoneId zo:" + zo);
        System.out.println("zonedDateTime:" + zonedDateTime);
        System.out.println();


    }


    /**
     * 比大小。
     *
     * @param v1
     * @param v2
     * @return
     */
    // 前提为v1、v2均不能为null
    public static boolean eq(BigDecimal v1, BigDecimal v2) {
        if (v1.compareTo(v2) == -1) {
            System.out.println("a小于b");
        }

        if (v1.compareTo(v2) == 0) {
            System.out.println("a等于b");
        }

        if (v1.compareTo(v2) == 1) {
            System.out.println("a大于b");
        }

        if (v1.compareTo(v2) > -1) {
            System.out.println("a大于等于b");
        }

        if (v1.compareTo(v2) < 1) {
            System.out.println("a小于等于b");
        }
        return true;
    }

    /**
     * 加法运算。
     *
     * @param v1 被加数
     */
    public static BigDecimal add(BigDecimal... v1) {
        BigDecimal result = new BigDecimal("0");
        for (BigDecimal b : v1) {
            result = calculate(result, b, ADDITION);
        }
        return result;
    }

    /**
     * 加法运算。
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static BigDecimal add(BigDecimal v1, BigDecimal v2) {

        return calculate(v1, v2, ADDITION);
    }

    /**
     * 加法运算。
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static BigDecimal add(float v1, float v2) {
        BigDecimal value1 = new BigDecimal(Float.toString(v1));
        BigDecimal value2 = new BigDecimal(Float.toString(v2));

        return calculate(value1, value2, ADDITION);
    }

    /**
     * 加法运算。
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static BigDecimal add(double v1, double v2) {
        BigDecimal value1 = new BigDecimal(v1 + "");
        BigDecimal value2 = new BigDecimal(v2 + "");

        return calculate(value1, value2, ADDITION);
    }

    /**
     * 减法运算
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static BigDecimal subtract(BigDecimal v1, BigDecimal v2) {

        return calculate(v1, v2, SUBTRACTION);
    }

    /**
     * 减法运算
     *
     * @param v1    被减数
     * @param v2    减数
     * @param scale 精确到多少位
     * @return 两个参数的差
     */
    public static BigDecimal subtract(BigDecimal v1, BigDecimal v2, int scale) {
        return calculate(v1, v2, SUBTRACTION, scale);
    }

    /**
     * 减法运算
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static BigDecimal subtract(float v1, float v2) {
        BigDecimal value1 = new BigDecimal(Float.toString(v1));
        BigDecimal value2 = new BigDecimal(Float.toString(v2));

        return calculate(value1, value2, SUBTRACTION);
    }

    /**
     * 减法运算
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static BigDecimal subtract(double v1, double v2) {
        BigDecimal value1 = new BigDecimal(v1 + "");
        BigDecimal value2 = new BigDecimal(v2 + "");
        return calculate(value1, value2, SUBTRACTION);
    }

    /**
     * 减法运算
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static double subtract(double v1, double... v2) {
        BigDecimal result = new BigDecimal(v1 + "");
        for (double d : v2) {
            result = calculate(result, new BigDecimal(d + ""), SUBTRACTION, SCALE_DEFAULT);
        }
        return result.doubleValue();
    }

    /**
     * 提供乘法运算。
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal multiply(BigDecimal v1, BigDecimal v2) {

        return calculate(v1, v2, MULTIPLICATION);
    }

    /**
     * 提供乘法运算。
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal multiply(BigDecimal v1, BigDecimal v2, int scale) {
        return calculate(v1, v2, MULTIPLICATION, scale);
    }

    /**
     * 提供乘法运算。
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal multiply(float v1, float v2) {
        BigDecimal value1 = new BigDecimal(Float.toString(v1));
        BigDecimal value2 = new BigDecimal(Float.toString(v2));

        return calculate(value1, value2, MULTIPLICATION);
    }

    /**
     * 提供乘法运算。
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal multiply(double v1, double v2) {
        BigDecimal value1 = new BigDecimal(v1 + "");
        BigDecimal value2 = new BigDecimal(v2 + "");

        return calculate(value1, value2, MULTIPLICATION);
    }

    /**
     * 乘以100 ，一般用于钱元转分时
     *
     * @param val 被乘数
     * @return 两个参数的积
     */
    public static BigDecimal multiply100(BigDecimal val) {
        BigDecimal value2 = new BigDecimal(ONE_HUNDRED);
        return calculate(val, value2, MULTIPLICATION, SCALE_ZERO);
    }


    /**
     * 提供（相对）精确的除法运算,默认精度小数点后两位
     *
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static BigDecimal divide(BigDecimal v1, BigDecimal v2) {

        return divide(v1, v2, SCALE_DEFAULT);
    }

    /**
     * 提供（相对）精确的除法运算
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static BigDecimal divide(BigDecimal v1, BigDecimal v2, int scale) {

        return calculate(v1, v2, DIVISION, scale);
    }

    /**
     * 提供（相对）精确的除法运算,默认精度小数点后两位
     *
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static BigDecimal divide(float v1, float v2) {

        return divide(v1, v2, SCALE_DEFAULT);
    }

    /**
     * 提供（相对）精确的除法运算
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static BigDecimal divide(float v1, float v2, int scale) {
        BigDecimal value1 = new BigDecimal(Float.toString(v1));
        BigDecimal value2 = new BigDecimal(Float.toString(v2));

        return calculate(value1, value2, DIVISION, scale);
    }

    /**
     * 提供（相对）精确的除法运算,默认精度小数点后两位
     *
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static BigDecimal divide(double v1, double v2) {

        return divide(v1, v2, SCALE_DEFAULT);
    }

    /**
     * 提供（相对）精确的除法运算,默认精度小数点后两位
     *
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static BigDecimal divide(String v1, String v2) {
        return divide(new BigDecimal(v1), new BigDecimal(v2), SCALE_DEFAULT);
    }

    /**
     * 提供（相对）精确的除法运算
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static BigDecimal divide(double v1, double v2, int scale) {
        BigDecimal value1 = new BigDecimal(Double.toString(v1));
        BigDecimal value2 = new BigDecimal(Double.toString(v2));
        return calculate(value1, value2, DIVISION, scale);
    }

    /**
     * 提供（相对）精确的除法运算,默认精度小数点后两位
     *
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static BigDecimal divide(int v1, int v2) {

        return divide(v1, v2, SCALE_DEFAULT);
    }

    /**
     * 提供（相对）精确的除法运算,默认精度小数点后两位
     *
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     */
    public static int divideToCeil(int v1, int v2) {
        BigDecimal n1 = new BigDecimal(v1 + "");
        BigDecimal n2 = new BigDecimal(v2 + "");
        return n1.divide(n2, BigDecimal.ROUND_UP).intValue();
    }

    /**
     * 提供（相对）精确的除法运算
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static BigDecimal divide(int v1, int v2, int scale) {
        BigDecimal value1 = new BigDecimal(Integer.toString(v1));
        BigDecimal value2 = new BigDecimal(Integer.toString(v2));

        return calculate(value1, value2, DIVISION, scale);
    }


    /**
     * 运算
     *
     * @param v1       运算值1
     * @param v2       运算值2
     * @param operator 操作符
     */
    private static BigDecimal calculate(BigDecimal v1, BigDecimal v2, String operator) {

        return calculate(v1, v2, operator, SCALE_DEFAULT);
    }


    /**
     * 运算
     *
     * @param v1       运算值1
     * @param v2       运算值2
     * @param operator 操作符
     * @param scale    精度
     */
    private static BigDecimal calculate(BigDecimal v1, BigDecimal v2, String operator, int scale) {
        if (null == v1 || null == v2) {
            return BigDecimal.ZERO;
        }
        if (null == operator || "".equals(operator.trim())) {
            return BigDecimal.ZERO;
        }
        if (scale < 0) {
            scale = SCALE_DEFAULT;
        }
        BigDecimal result = BigDecimal.ZERO;
        switch (operator) {
            case ADDITION:
                result = v1.add(v2);
                break;
            case SUBTRACTION:
                result = v1.subtract(v2);
                break;
            case MULTIPLICATION:
                result = v1.multiply(v2);
                break;
            case DIVISION:
                result = v1.divide(v2, scale, BigDecimal.ROUND_HALF_UP);
                break;
            default:
                break;
        }

        return result.setScale(scale, BigDecimal.ROUND_HALF_UP);
    }


    /**
     * 元转分
     *
     * @param amount
     * @return
     */
    public static BigDecimal changeY2F(BigDecimal amount) {
        return amount.multiply(new BigDecimal(ONE_HUNDRED));
    }


    /**
     * 分转元
     *
     * @param amount
     * @return
     */
    public static BigDecimal changeF2Y(int amount) {
        return BigDecimal.valueOf(Integer.valueOf(amount)).divide(new BigDecimal(ONE_HUNDRED));
    }

    /**
     * 分转元
     *
     * @param amount
     * @return
     */
    public static BigDecimal changeF2Y(String amount) {
        if (!NumberUtil.isNumber(amount)) {
            return null;
        }
        return new BigDecimal(amount).divide(new BigDecimal(ONE_HUNDRED));
    }
}
